#!/usr/bin/perl
# example zfs_pool_alert script
# ###########################################################################
# To be copied into /etc/zfs/zfs_pool_alert (and made executable) if you plan
# on using automatic recovery on vdev failure.

# ###########################################################################
# zfs_pool_alert : this command is executed everytime a pool passes from the
# state HEALTHY to DEGRADED, or the other way around.
# It receives only the pool name as argument.
# Here this script tries to :
#  - if the new state is degraded
#    - if there are some spares available, try to use 1
#    - otherwise send a mail to tell a vdev is failing for this pool
#  - if the new state is healthy
#    - if there are still some spares in use, execute zpool clear, spares are
#    usually removed quite some time after that without sending any command.

use strict;

my $zpool = "zpool";
my $pool = shift @ARGV;
my $status = `$zpool status $pool`;

my ($state) = $status =~ /state: (.+)/;

if ($state eq "DEGRADED") {
	# get the 1st avail spare
	my ($spare) = $status =~ /[ \t]+(.+)[ \t]+AVAIL/; 
	my ($dev, $cur, $comment) = $status =~ /[ \t]+(.+)[ \t]+(UNAVAIL|FAULTED)[ \t\d]+(.+)/;
	if ($spare) {
		$spare = "/dev/$spare" if ($spare !~ /\//);
		if ($dev) {
			print "dev :$dev:\n";
			if ($comment =~ /was (.+)/) {
				$dev = $1;
			}
			$dev = "/dev/$dev" if ($dev !~ /\//);
			system("$zpool replace $pool $dev $spare");
		} else {
			print "no dev $dev,$cur,$comment\n";
		}
	} else {
# a vdev is failing and there is no spare -> send a mail ?
		system("echo automatic mail generated by zfs_pool_alert | ".
		"mail -s 'vdev $dev is failing for pool $pool' root");
	}
} else {
	my ($spare) = $status =~ /[ \t]+(.+)[ \t]+INUSE/; 
	if ($spare) {
# There is a spare in use, but the pool is already healthy, be sure to clear
# the errors then. Notice : the spare is not removed immediately then, and it
# can be quite long. Don't know why !
		sleep 3;
		system("$zpool clear $pool");
	}
}





